package com.modxiageasyh.coroutinedemolibrary

import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.fragment_coroutines.*
import kotlinx.coroutines.*

// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
private const val TAG = "CoroutinesFragment"

/**
 * A simple [Fragment] subclass.
 * Use the [CoroutinesFragment.newInstance] factory method to
 * create an instance of this fragment.
 */
class CoroutinesFragment : Fragment() {
    // TODO: Rename and change types of parameters
    private var param1: String? = null
    private var param2: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_coroutines, container, false)
    }

    private val mScope = MainScope()
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val job1 = mScope.launch {
            // 这里就是协程体

            // 延迟1000毫秒  delay是一个挂起函数
            // 在这1000毫秒内该协程所处的线程不会阻塞
            // 协程将线程的执行权交出去，该线程该干嘛干嘛，到时间后会恢复至此继续向下执行
            delay(1000)
        }

        // 创建一个指定了调度模式的协程，该协程的运行线程为IO线程
        val job2 = mScope.launch(Dispatchers.IO) {

            Log.d(TAG, "onViewCreated: 1111")
            // 此处是IO线程模式
            delay(5000)
            // 切线程 将协程所处的线程环境切至指定的调度模式Main
            withContext(Dispatchers.Main) {
                // 现在这里就是Main线程了  可以在此进行UI操作了
                Log.d(TAG, "onViewCreated: job2=")
            }
        }
        // 下面直接看一个例子： 从网络中获取数据  并更新UI
        // 该例子不会阻塞主线程
        mScope.launch(Dispatchers.IO) {
            // 执行getUserInfo方法时会将线程切至IO去执行
            val userInfo = getUserInfo()
            // 获取完数据后 切至Main线程进行更新UI
            withContext(Dispatchers.Main) {
                // 更新UI
                tv1.text = userInfo
                Log.d(TAG, "onViewCreated: 333")
            }
        }
        val 还行 = "baizu"
        tv1.text = "dd"
        lifecycleScope.launch(Dispatchers.IO) {
            Log.d(TAG, "onViewCreated: banzai$还行")
            delay(20000)
            withContext(Dispatchers.Main) {
                Log.d(TAG, "onViewCreated: aah")
            }
        }

        btn1.setOnClickListener {
            asyncTest()
        }
        btn2.setOnClickListener {
            asyncTest2()
        }

        ll_btn1.setOnClickListener {
            if (ll_btn2.visibility == View.VISIBLE) {
                ll_btn2.visibility = View.GONE
            } else {
                ll_btn2.visibility = View.VISIBLE
            }
        }
        cl_btn.setOnClickListener {
            if (ll.visibility == View.VISIBLE) {
                ll.visibility = View.GONE
            } else {
                ll.visibility = View.VISIBLE
            }
        }
    }


    private fun asyncTest() {
        mScope.launch {
            // 开启一个IO模式的线程 并返回一个Deferred，Deferred可以用来获取返回值
            // 代码执行到此处时会新开一个协程 然后去执行协程体  父协程的代码会接着往下走
            val deferred = async(Dispatchers.IO) {
                Log.d(TAG, "asyncTest: io")
                // 模拟耗时
                delay(2000)
                Log.d(TAG, "asyncTest: io2")
                // 返回一个值
                "Quyunshuo"
            }
            val deferred2 = async(Dispatchers.Default) {
                Log.d(TAG, "asyncTest: Default")
                // 模拟耗时
                delay(2000)
                // 返回一个值
                Log.d(TAG, "asyncTest: Default2")
                "   Quyunshuo2"
            }
            // 等待async执行完成获取返回值 此处并不会阻塞线程  而是挂起 将线程的执行权交出去
            // 等到async的协程体执行完毕后  会恢复协程继续往下执行
            Log.d(TAG, "asyncTest: 3")
            val date = deferred.await()+deferred2.await()
            Log.d(TAG, "asyncTest: 4")
            tv1.setText(date)
        }
    }
    fun asyncTest2() {
        mScope.launch {
            // 此处有一个需求  同时请求5个接口  并且将返回值拼接起来

            Log.d(TAG, "asyncTest2: ")
            val job1 = async {
                // 请求1
                delay(16000)
                "1"
            }
            val job2 = async {
                // 请求2
                delay(9000)
                "2"
            }
            val job3 = async {
                // 请求3
                delay(5000)
                "3"
            }
            val job4 = async {
                // 请求4
                delay(2000)
                "4"
            }
            val job5 = async {
                // 请求5
                delay(7000)
                "5"
            }

            // 代码执行到此处时  5个请求已经同时在执行了
            // 等待各job执行完 将结果合并
            Log.d(
                TAG,
                "asyncTest2: ${job1.await()} ${job2.await()} ${job3.await()} ${job4.await()} ${job5.await()}"
            )
            tv1.setText("asyncTest2: ${job1.await()} ${job2.await()} ${job3.await()} ${job4.await()} ${job5.await()}")
            // 因为我们设置的模拟时间都是5000毫秒  所以当job1执行完时  其他job也均执行完成
        }
    }

    companion object {
        /**
         * Use this factory method to create a new instance of
         * this fragment using the provided parameters.
         *
         * @param param1 Parameter 1.
         * @param param2 Parameter 2.
         * @return A new instance of fragment CoroutinesFragment.
         */
        // TODO: Rename and change types and number of parameters
        @JvmStatic
        fun newInstance(param1: String, param2: String) =
            CoroutinesFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }

    private suspend fun getUserInfo(): String {
        return withContext(Dispatchers.IO) {
            delay(2000)
            "Kotlin"
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        // 取消协程 防止协程泄漏  如果使用lifecycleScope则不需要手动取消
        mScope.cancel()
    }
}